/**
 * FileName: NettyClient
 * Author: Mr.Wang
 * Date: 2025/10/15 15:16
 * Description:
 * History:
 */
package org.test.device.config.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * 〈一句话功能简述〉<br>
 * 〈netty客户端〉
 * 测试发送信息
 * @author Mr.Wang
 * @create 2025/10/15
 * @since 1.0.0
 */
@Slf4j
public class NettyClient {
    private final String host;
    private final int port;
    private final String message;

    public NettyClient(String host, int port, String message) {
        this.host = host;
        this.port = port;
        this.message = message;
    }

    public void connect() throws Exception {
        // 1. 创建客户端 EventLoopGroup
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            // 2. 客户端启动辅助类
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    // 3. 指定客户端通道实现
                    .channel(NioSocketChannel.class)
                    // 4. 禁用 Nagle 算法，立即发送数据
                    .option(ChannelOption.TCP_NODELAY, true)
                    // 5. 初始化通道处理器
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            // 关键：添加与服务器对应的编解码器，统一消息格式
                            ch.pipeline()
                                    // 1. 解码器：与服务器保持一致，解析带长度字段的消息
                                    .addLast(new LengthFieldBasedFrameDecoder(
                                            65535,  // 最大帧长度（与服务器maxFrameLength一致）
                                            0, 4,   // 长度字段偏移量0，占用4字节
                                            0, 4))  // 跳过长度字段4字节
                                    // 2. 编码器：自动在消息前添加4字节长度字段
                                    .addLast(new LengthFieldPrepender(4))
                                    // 3. 字符串编解码器（与服务器一致）
                                    .addLast(new StringDecoder(StandardCharsets.UTF_8))
                                    .addLast(new StringEncoder(StandardCharsets.UTF_8))
                                    // 4. 自定义客户端处理器
                                    .addLast(new ClientHandler());
                        }
                    });

            // 6. 连接服务器
            ChannelFuture future = bootstrap.connect(host, port).sync();
            log.info("客户端已连接到服务器：{}:{}", host, port);

            // 7. 发送消息
            future.channel().writeAndFlush(message);
            log.info("已发送消息：{}", message);

            // 8. 等待连接关闭
            future.channel().closeFuture().sync();
            log.info("客户端已关闭连接");
        } finally {
            // 9. 优雅关闭
            group.shutdownGracefully();
            log.info("客户端EventLoopGroup已关闭");
        }
    }

    /**
     * 测试方法：连接服务器并发送消息
     */
    public static void main(String[] args) throws Exception {
        String message = "Hello, Netty Server! 你已经是一个成熟的技术了，要学会自己解决问题！";
        Scanner scanner = new Scanner(System.in);
        message = scanner.nextLine();
        new NettyClient("localhost", 8888, message).connect();
    }
}